//
//  AirFireWorksRender.h
//  AirCpp
//
//  Created by Penghao on 14-3-7.
//  Copyright (c) 2014年 PengHao. All rights reserved.
//

#ifndef __AirCpp__AirFireWorksRender__
#define __AirCpp__AirFireWorksRender__

#include <iostream>

#include "../AirSDK/AirGraphics/AirGraphics.h"
#include "../AirSDK/AirGraphics/AirGraphicsRender.h"
#include "../AirSDK/AirGraphics/AirGraphicsUtility.h"
#define PI 3.14159
using namespace AirCpp;
class FireWorksRender : public AirGraphicsRender{
public:
    GLint colorLoc;
    //hardware performers
    GLint performLevel;
    GLint particleNum;
    GLint particleSize;
    // Attribute locations
    GLint  lifetimeLoc;
    GLint  startPositionLoc;
    GLint  endPositionLoc;
    // Uniform location
    GLint timeLoc;
    GLint centerPositionLoc;
    GLint samplerLoc;
    // Texture handle
    GLuint textureId;
    // Particle vertex data
    GLfloat *particleData;
    // Current time
    GLfloat time;
    GLfloat color[4];
    // buffer data
    GLuint offset;
    GLuint vboIds;
    
    GLint projectionLoc;
    //    AGCoordinate projection;
    GLfloat projection[16];
    
public:
    int init(AirCoordinate3D *coordinate_, AirPosition3D *center_, GLchar *vshfilename, GLchar *fshfilename, GLint _performLevel){
        if (AirGraphicsRender::init(coordinate_, center_, vshfilename, fshfilename) == -1) {
            return -1;
        }
        performLevel = _performLevel;
        particleNum = 5000;
        particleSize = 7;
        offset = 0;
        particleData = (GLfloat *) malloc(sizeof(GLfloat)*particleNum * particleSize);
        
        //  映射为正常显示
        GLfloat _projection[16] = {
            1.0f, 0.0f, 0.0f, 0.0f,
            0.0f, 16.0/23.0, 0.0f, 0.0f,
            0.0f, 0.0f, 1.0f, 0.0f,
            0.0f, 0.0f, 0.0f, 1.0f };
        memcpy(projection, _projection, sizeof(GLfloat)*16);
        
        // Initialize time to cause reset on first update
        this->time = 1.0f;
        
        // Pick a new start location and color
        this->color[3] = 0.5;
        
        // Fill in particle data array
        srand (rand());
        GLfloat x, y, z, anglex, angley;
        for (GLint i = 0; i < this->particleNum; i++ )
        {
            anglex = ( (float)(rand() % 20000) / 10000.0f );
            angley = ( (float)(rand() % 20000) / 10000.0f );
            
            x = (GLfloat)cos(anglex*PI);
            y = (GLfloat)cos(angley*PI);
            if (pow(x,2)+pow(y,2)>1) {
                i--;
                continue;
            }else {
                z = (GLfloat)sqrt(1-pow(x,2)-pow(y,2));
                
                GLfloat *particleData = this->particleData+i * this->particleSize;
                
                // Lifetime of particle
                (*particleData++) = ( (GLfloat)(rand() % 40000) / 10000.0f );
                
                // End position of particle
                (*particleData++) = 0.25f*x;
                (*particleData++) = 0.25f*y;
                (*particleData++) = 0.25f*z;
                
                // Start position of particle
                (*particleData++) = 0.0f;
                (*particleData++) = 0.0f;
                (*particleData++) = 0.0f;
            }
        }
        this->color[0] = 0.0;
        this->color[1] = 1.0;
        this->color[2] = 1.0;
        this->color[3] = 0.5;
        GLchar textureName[] = "white";
        this->textureId = loadTexture (textureName);
        AirAssert(this->textureId > 0);
        return 0;
    }
    
    void bind_buffer(void){
        //setBuffer point
        glGenBuffers(1, &this->vboIds);
        //bind Buffer to the point
        glBindBuffer(GL_ARRAY_BUFFER, this->vboIds);
        //set buffer data
        glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat)*this->particleNum * this->particleSize, this->particleData, GL_STATIC_DRAW);
    }
    
    ~FireWorksRender(void){
        free(particleData);
        particleData = NULL;
    }
    
    
    void bind_shader_atrribute(GLuint shaderProgram){
        // Get the attribute locations
        this->lifetimeLoc = glGetAttribLocation (shaderProgram, "a_lifetime" );
        this->startPositionLoc = glGetAttribLocation (shaderProgram, "a_startPosition" );
        this->endPositionLoc = glGetAttribLocation (shaderProgram, "a_endPosition" );
        
        // Get the uniform locations
        this->timeLoc = glGetUniformLocation (shaderProgram, "u_time" );
        this->projectionLoc = glGetUniformLocation(shaderProgram, "Projection" );
        this->centerPositionLoc = glGetUniformLocation (shaderProgram, "u_centerPosition" );
        this->colorLoc = glGetUniformLocation (shaderProgram, "u_color" );
        this->samplerLoc = glGetUniformLocation (shaderProgram, "s_texture" );
    }

    void update(bool forcibly)
    {
        time += 0.05;
        time += 0.05;
        if (time >= 8.0f) {
            time = 0.0f;
        } else {
        }
        for (int i = 0 ; i < render_array.count(); ++i) {
            render_array[i]->update(forcibly);
        }
    }
    
    void render(){
        // Use the program object
        glUseProgram (program);
        int num = 1;
        if (performLevel>0) {
            num = performLevel*2;
        }else if (performLevel<-5) {
            num = 0;
        }
        
        //#warning crash at here
        glBindBuffer(GL_ARRAY_BUFFER, vboIds);
        glEnableVertexAttribArray ( lifetimeLoc );
        glEnableVertexAttribArray ( endPositionLoc );
        glEnableVertexAttribArray ( startPositionLoc );
        
        // Blend particles
        glEnable ( GL_BLEND );
        glBlendFunc ( GL_SRC_ALPHA, GL_ONE );
        
        // Bind the texture
        glActiveTexture ( GL_TEXTURE0 );
        glBindTexture ( GL_TEXTURE_2D, textureId );
        glEnable ( GL_TEXTURE_2D );
        
        // Set the sampler texture unit to 0
        glUniform1i ( samplerLoc, 0 );
        glUniformMatrix4fv(projectionLoc, 1, 0, &projection[0]);
        for (int j=0; j<num; j++) {
            // Load the vertex attributes
            glUniform3fv ( centerPositionLoc, 1, (float *) center->elements);
            glUniform4fv ( colorLoc, 1, &color[0] );
            // Load uniform time variable
            glUniform1f ( timeLoc, time+0.03*j );
            // Load the vertex attributes
            offset = 0;
            glVertexAttribPointer ( lifetimeLoc, 1, GL_FLOAT,
                                   GL_FALSE, particleSize * sizeof(GLfloat),
                                   (const void*)offset );
            offset += sizeof(GLfloat);
            glVertexAttribPointer ( endPositionLoc, 3, GL_FLOAT,
                                   GL_FALSE, particleSize * sizeof(GLfloat),
                                   (const void*)offset );
            offset += 3*sizeof(GLfloat);
            glVertexAttribPointer ( startPositionLoc, 3, GL_FLOAT,
                                   GL_FALSE, particleSize * sizeof(GLfloat),
                                   (const void*)offset );
            glDrawArrays( GL_POINTS, 0, particleNum );
        }
        
        //must disable AttribArray and binded buffer
        glDisableVertexAttribArray ( lifetimeLoc );
        glDisableVertexAttribArray ( endPositionLoc );
        glDisableVertexAttribArray ( startPositionLoc );
        glBindBuffer(GL_ARRAY_BUFFER, NULL);
        
        
        for (int i = 0 ; i < render_array.count(); ++i) {
            render_array[i]->render();
        }
};


};

#endif /* defined(__AirCpp__AirFireWorksRender__) */
